home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 684 / 684.xpi / chrome / fireftp.jar / content / js / remote / remoteTree.js < prev   
Text File  |  2009-09-28  |  50KB  |  1,307 lines

  1. var remoteTree = {
  2.   data          : new Array(),
  3.   displayData   : new Array(),
  4.   rowCount      : 0,
  5.   remoteSize    : 0,
  6.   searchMode    : 0,
  7.   isEditing     : false,
  8.   editType      : "",
  9.   editParent    : null,
  10.   extraCallback : null,
  11.   errorCallback : null,
  12.   rememberSort  : null,
  13.   isLoading     : true,
  14.  
  15.   getParentIndex      : function(row)               { return -1; },
  16.   getLevel            : function(row)               { return 0;  },
  17.   getRowProperties    : function(row, props)        { },
  18.   getColumnProperties : function(colid, col, props) { },
  19.   isContainer         : function(row)               { return false; },
  20.   isSeparator         : function(row)               { return false; },
  21.   isSorted            : function(row)               { return false; },
  22.   setTree             : function(treebox)           { this.treebox = treebox; },
  23.  
  24.   getCellText         : function(row, column)       {                                          // text for the files
  25.     if (row >= 0 && row < this.data.length) {
  26.       switch (column.id) {
  27.         case "remotename":
  28.           return this.searchMode == 2 ? this.displayData[row].path : this.displayData[row].leafName;
  29.         case "remotesize":
  30.           return this.displayData[row].fileSize;
  31.         case "remotedate":
  32.           return this.displayData[row].date;
  33.         case "remotetype":
  34.           return this.displayData[row].extension;
  35.         case "remoteattr":
  36.           return this.displayData[row].attr;
  37.         default:
  38.           return " ";
  39.       }
  40.     }
  41.  
  42.     return "";
  43.   },
  44.  
  45.   getImageSrc : function(row, col) {
  46.     return row >= 0 && row < this.data.length && col.id == "remotename" && this.displayData[row].icon ? this.displayData[row].icon : "";
  47.   },
  48.  
  49.   cycleHeader : function(col) {
  50.     var sortDirection = col.element.getAttribute("sortDirection") == "descending"
  51.                      || col.element.getAttribute("sortDirection") == "natural"  ? "ascending" : "descending";
  52.     $('remotename').setAttribute("sortDirection", "natural");
  53.     $('remotesize').setAttribute("sortDirection", "natural");
  54.     $('remotedate').setAttribute("sortDirection", "natural");
  55.     $('remotetype').setAttribute("sortDirection", "natural");
  56.     $('remoteattr').setAttribute("sortDirection", "natural");
  57.     col.element.setAttribute(    "sortDirection", sortDirection);
  58.     this.sort();
  59.   },
  60.  
  61.   getCellProperties : function(row, col, props)   {
  62.     if (row >= 0 && row < this.data.length) {
  63.       if (col.id == "remotename") {
  64.         if (this.data[row].isDirectory()) {
  65.           props.AppendElement(gAtomService.getAtom("isFolder"));
  66.         } else if (this.data[row].isSymlink()) {
  67.           props.AppendElement(gAtomService.getAtom("isLink"));
  68.         }
  69.  
  70.         props.AppendElement(gAtomService.getAtom("nameCol"));
  71.       }
  72.  
  73.       if (dragObserver.overName && this.data[row].isDirectory()) {
  74.         props.AppendElement(gAtomService.getAtom("overName"));
  75.       }
  76.  
  77.       if (!gFtp.isConnected) {
  78.         props.AppendElement(gAtomService.getAtom("disconnected"));
  79.       }
  80.  
  81.       if (this.data[row].isHidden) {
  82.         props.AppendElement(gAtomService.getAtom("hidden"));
  83.       }
  84.  
  85.       if (this.data[row].isCut) {
  86.         props.AppendElement(gAtomService.getAtom("cut"));
  87.       }
  88.     }
  89.   },
  90.  
  91.   // ****************************************************** updateView ***************************************************
  92.  
  93.   updateView : function(skipCache) {
  94.     this.isLoading = true;
  95.     gFtp.list(gRemotePath.value, "remoteTree.updateView2()", skipCache);
  96.   },
  97.  
  98.   updateView2 : function(files) {
  99.     var remoteTreeItems;
  100.     var firstSearch;
  101.  
  102.     this.isLoading = false;
  103.  
  104.     if (!files) {
  105.       this.searchMode = 0;
  106.       gRemoteTreeChildren.removeAttribute('search')
  107.  
  108.       remoteTreeItems    = gFtp.listData;
  109.       this.remoteSize    = 0;                                                                     // get directory size
  110.  
  111.       for (var x = 0; x < remoteTreeItems.length; ++x) {
  112.         this.remoteSize += remoteTreeItems[x].fileSize;
  113.       }
  114.  
  115.       this.remoteSize    = parseSize(this.remoteSize);
  116.       this.data          = remoteTreeItems;
  117.     } else {
  118.       if (this.remoteSize != -1) {
  119.         this.data        = new Array();
  120.         this.displayData = new Array();
  121.         this.treebox.rowCountChanged(0, -this.rowCount);
  122.  
  123.         this.rememberSort = { cols : ["remotename", "remotesize", "remotedate", "remotetype", "remoteattr"],
  124.                               vals : [$('remotename').getAttribute("sortDirection"),
  125.                                       $('remotesize').getAttribute("sortDirection"),
  126.                                       $('remotedate').getAttribute("sortDirection"),
  127.                                       $('remotetype').getAttribute("sortDirection"),
  128.                                       $('remoteattr').getAttribute("sortDirection")] };
  129.       }
  130.  
  131.       files.sort(compareName);
  132.  
  133.       for (var x = 0; x < files.length; ++x) {
  134.         this.data.push(files[x]);
  135.       }
  136.  
  137.       this.remoteSize  = -1;
  138.       this.searchMode  = this.searchMode ? this.searchMode : (gSearchRecursive ? 2 : 1);
  139.       gRemoteTreeChildren.setAttribute('search', true);
  140.     }
  141.  
  142.     this.sort(files);                                                                           // update remoteTree
  143.  
  144.     var index = remoteDirTree.indexOfPath(gRemotePath.value);                                   // select directory in remoteDirTree
  145.     remoteDirTree.selection.select(index);
  146.     remoteDirTree.treebox.ensureRowIsVisible(index);
  147.  
  148.     if (this.data.length && !files) {
  149.       this.selection.select(0);                                                                 // select first element in remoteTree
  150.     }
  151.  
  152.     this.mouseOver(null);
  153.  
  154.     if (files) {
  155.       return;
  156.     }
  157.  
  158.     var anyFolders = false;                                                                     // see if the folder has any subfolders
  159.     for (var x = 0; x < this.data.length; ++x) {
  160.       if (this.data[x].isDirectory()) {
  161.         anyFolders = true;
  162.         break;
  163.       }
  164.     }
  165.  
  166.     if (!anyFolders) {                                                                          // and if there are no subfolders then update our tree
  167.       if (remoteDirTree.data[index].open) {                                                     // if remoteDirTree is open
  168.         remoteDirTree.toggleOpenState(index);
  169.       }
  170.  
  171.       remoteDirTree.data[index].empty     = true;
  172.       remoteDirTree.data[index].open      = false;
  173.       remoteDirTree.data[index].children  = null;
  174.  
  175.       for (var x = 0; x < remoteDirTree.dirtyList.length; ++x) {
  176.         if (remoteDirTree.dirtyList[x] == gRemotePath.value) {
  177.           remoteDirTree.dirtyList.splice(x, 1);
  178.           break;
  179.         }
  180.       }
  181.     } else if (anyFolders && remoteDirTree.data[index].empty) {
  182.       remoteDirTree.data[index].empty     = false;
  183.     }
  184.  
  185.     remoteDirTree.treebox.invalidateRow(index);
  186.  
  187.     if (this.extraCallback) {
  188.       var tempCallback   = this.extraCallback;
  189.       var func = function() { tempCallback(); };
  190.       this.extraCallback = null;
  191.       setTimeout(func, 0);
  192.     }
  193.   },
  194.  
  195.   sort : function(files) {
  196.     if (!files) {
  197.       if (this.rememberSort) {
  198.         for (var x = 0; x < this.rememberSort.cols.length; ++x) {
  199.           $(this.rememberSort.cols[x]).setAttribute("sortDirection", this.rememberSort.vals[x]);
  200.         }
  201.  
  202.         this.rememberSort = null;
  203.       }
  204.  
  205.       this.sortHelper($('remotename'), this.searchMode == 2 ? directorySort2 : compareName);
  206.       this.sortHelper($('remotesize'), compareSize);
  207.       this.sortHelper($('remotedate'), compareDate);
  208.       this.sortHelper($('remotetype'), compareType);
  209.       this.sortHelper($('remoteattr'), compareRemoteAttr);
  210.  
  211.       this.displayData = new Array();
  212.     } else {
  213.       $('remotename').setAttribute("sortDirection", "natural");
  214.       $('remotesize').setAttribute("sortDirection", "natural");
  215.       $('remotedate').setAttribute("sortDirection", "natural");
  216.       $('remotetype').setAttribute("sortDirection", "natural");
  217.       $('remoteattr').setAttribute("sortDirection", "natural");
  218.     }
  219.  
  220.     var start = files ? this.data.length - files.length : 0;
  221.  
  222.     for (var row = start; row < this.data.length; ++row) {
  223.       this.displayData.push({ leafName : this.data[row].leafName,
  224.                               fileSize : this.getFormattedFileSize(row),
  225.                               date     : this.data[row].date,
  226.                               extension: this.data[row].isDirectory() ? "" : this.getExtension(this.data[row].leafName),
  227.                               attr     : this.data[row].permissions,
  228.                               icon     : this.getFileIcon(row),
  229.                               path     : this.data[row].path });
  230.     }
  231.  
  232.     if (files) {
  233.       this.rowCount = this.data.length;
  234.       this.treebox.rowCountChanged(start, files.length);
  235.     } else {
  236.       this.treebox.rowCountChanged(0, -this.rowCount);
  237.       this.rowCount = this.data.length;
  238.       this.treebox.rowCountChanged(0, this.rowCount);
  239.     }
  240.   },
  241.  
  242.   sortHelper : function(el, sortFunc) {
  243.     if (el.getAttribute("sortDirection") && el.getAttribute("sortDirection") != "natural") {
  244.       this.data.sort(sortFunc);
  245.  
  246.       if (!gPrefs.getBoolPref("remotesortfix")) {   // blah, fix dumb mistake that changed descending into ascending
  247.         el.setAttribute("sortDirection", "ascending");
  248.         gPrefs.setBoolPref("remotesortfix", true);
  249.       }
  250.  
  251.       if (el.getAttribute("sortDirection") == "descending") {
  252.         this.data.reverse();
  253.       }
  254.     }
  255.   },
  256.  
  257.   getFormattedFileSize : function(row) {
  258.     if (!this.data[row].fileSize) {
  259.       return gBytesMode ? "0  " : gStrbundle.getFormattedString("kilobyte", ["0"]) + "  ";
  260.     }
  261.  
  262.     if (gBytesMode) {
  263.       return commas(this.data[row].fileSize) + "  ";
  264.     }
  265.  
  266.     return gStrbundle.getFormattedString("kilobyte", [commas(Math.ceil(this.data[row].fileSize / 1024))]) + "  ";
  267.   },
  268.  
  269.   getExtension : function(leafName) {
  270.     return leafName.lastIndexOf(".") != -1 ? leafName.substring(leafName.lastIndexOf(".") + 1, leafName.length).toLowerCase() : "";
  271.   },
  272.  
  273.   getFileIcon : function(row) {
  274.     return this.data[row].isDirectory() || this.data[row].isSymlink() ? "" :  "moz-icon://" + this.data[row].leafName + "?size=16";
  275.   },
  276.  
  277.   // ****************************************************** refresh ***************************************************
  278.  
  279.   refresh : function() {
  280.     if (!gFtp.isConnected) {
  281.       return;
  282.     }
  283.  
  284.     if (remoteDirTree.selection.currentIndex == -1) {
  285.       return;
  286.     }
  287.  
  288.     if (remoteDirTree.data[remoteDirTree.selection.currentIndex].open) {                        // if remoteDirTree is open
  289.       gFtp.removeCacheEntry(remoteDirTree.data[remoteDirTree.selection.currentIndex].path);     // clear out cache entry
  290.       gFtp.list(remoteDirTree.data[remoteDirTree.selection.currentIndex].path, "remoteTree.refreshCallback(" + remoteDirTree.selection.currentIndex + ")", false);      // get data for this directory
  291.     } else {
  292.       remoteDirTree.data[remoteDirTree.selection.currentIndex].empty    = false;                // not empty anymore
  293.       remoteDirTree.data[remoteDirTree.selection.currentIndex].children = null;                 // reset its children
  294.       remoteDirTree.treebox.invalidateRow(remoteDirTree.selection.currentIndex);
  295.       this.updateView(true);
  296.     }
  297.   },
  298.  
  299.   refreshCallback : function(index) {
  300.     remoteDirTree.toggleOpenState(remoteDirTree.selection.currentIndex);                      // close it up
  301.     remoteDirTree.data[remoteDirTree.selection.currentIndex].children = null;                 // reset its children
  302.     remoteDirTree.updateViewAfter = true;                                                     // refresh remoteTree afterwards
  303.     remoteDirTree.toggleOpenState2(index);                                                    // and open it up again
  304.   },
  305.  
  306.   // ************************************************* file functions ***************************************************
  307.  
  308.   launch : function() {
  309.     if (this.selection.count == 0 || !gFtp.isConnected || !isReady()) {
  310.       return;
  311.     }
  312.  
  313.     try {
  314.       var count = 0;
  315.  
  316.       for (var x = 0; x < remoteTree.rowCount; ++x) {
  317.         if (remoteTree.selection.isSelected(x)) {
  318.           ++count;
  319.  
  320.           let tmpFile = Components.classes["@mozilla.org/file/directory_service;1"].createInstance(Components.interfaces.nsIProperties).get("TmpD", Components.interfaces.nsILocalFile);
  321.           tmpFile.append(count + '-' + remoteTree.data[x].leafName);
  322.           while (tmpFile.exists()) {
  323.             ++count;
  324.             tmpFile.leafName = count + '-' + remoteTree.data[x].leafName;
  325.           }
  326.  
  327.           count = 0;
  328.  
  329.           let remoteFile = remoteTree.data[x];
  330.  
  331.           var func = function() {
  332.             var subFunc = function() { launchProgram(null, null, tmpFile, remoteFile); };
  333.             setTimeout(subFunc, 0);                                                                     // let the queue finish up
  334.           };
  335.  
  336.           gFtp.download(remoteFile.path, tmpFile.path, remoteFile.fileSize, false, 0, false, func);
  337.         }
  338.       }
  339.     } catch (ex) {
  340.       debug(ex);
  341.     }
  342.  
  343.   },
  344.  
  345.   openContainingFolder : function() {
  346.     if (!isReady() || this.selection.currentIndex < 0 || this.selection.currentIndex >= this.rowCount) {
  347.       return;
  348.     }
  349.  
  350.     remoteDirTree.changeDir(this.data[this.selection.currentIndex].parent.path);
  351.   },
  352.  
  353.   viewOnTheWeb : function() {
  354.     if (!gFtp.isConnected || this.selection.count == 0) {
  355.       return;
  356.     }
  357.  
  358.     if (!gWebHost) {
  359.       doAlert(gStrbundle.getString("fillInWebHost"));
  360.       return;
  361.     }
  362.  
  363.     if (this.selection.currentIndex < 0 || this.selection.currentIndex >= this.rowCount) {
  364.       this.selection.currentIndex = this.rowCount - 1;
  365.     }
  366.  
  367.     for (var x = 0; x < remoteTree.rowCount; ++x) {
  368.       if (remoteTree.selection.isSelected(x)) {
  369.         var path = this.data[x].path;
  370.  
  371.         if (gPrefix && path.indexOf(gPrefix) == 0) {
  372.           path = path.substring(gPrefix.length);
  373.         }
  374.  
  375.         runInFirefox(gWebHost + escape(gFtp.fromUTF8.ConvertFromUnicode(path) + gFtp.fromUTF8.Finish()));
  376.       }
  377.     }
  378.   },
  379.  
  380.   copyUrl : function(http, login) {
  381.     if (!gFtp.isConnected || this.selection.count == 0) {
  382.       return;
  383.     }
  384.  
  385.     if (http && !gWebHost) {
  386.       doAlert(gStrbundle.getString("fillInWebHost"));
  387.       return;
  388.     }
  389.  
  390.     if (this.selection.currentIndex < 0 || this.selection.currentIndex >= this.rowCount) {
  391.       this.selection.currentIndex = this.rowCount - 1;
  392.     }
  393.  
  394.     var paths = "";
  395.  
  396.     for (var x = 0; x < remoteTree.rowCount; ++x) {
  397.       if (remoteTree.selection.isSelected(x)) {
  398.         var path = this.data[x].path;
  399.  
  400.         if (http && gPrefix && path.indexOf(gPrefix) == 0) {
  401.           path = path.substring(gPrefix.length);
  402.         }
  403.  
  404.         path = http ? gWebHost + escape(gFtp.fromUTF8.ConvertFromUnicode(path) + gFtp.fromUTF8.Finish())
  405.                     : 'ftp://' + (login ? encodeURIComponent(gFtp.login) + ':' + gFtp.password + '@' : '')
  406.                                + gFtp.host + (gFtp.port == 21 ? '' : ':' + gFtp.port) + path;
  407.  
  408.         paths += (paths ? '\n' : '') + path;
  409.       }
  410.     }
  411.  
  412.  
  413.     var clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"].createInstance(Components.interfaces.nsIClipboardHelper);
  414.     clipboard.copyString(paths);
  415.   },
  416.  
  417.   create : function(isDir) {
  418.     if (!gFtp.isConnected || !isReady() || this.searchMode == 2) {
  419.       return;
  420.     }
  421.  
  422.     this.data.push({        leafName    : "",
  423.                             fileSize    : "",
  424.                             date        : "",
  425.                             extension   : "",
  426.                             attr        : "",
  427.                             path        : "",
  428.                             isDir       : isDir,
  429.                             isDirectory : function() { return true  },
  430.                             isSymlink   : function() { return false },
  431.                             isHidden    : false });
  432.     this.displayData.push({ leafName    : "",
  433.                             fileSize    : "",
  434.                             date        : "",
  435.                             extension   : "",
  436.                             attr        : "",
  437.                             icon        : isDir ? "" : "moz-icon://file?size=16",
  438.                             path        : "" });
  439.     ++this.rowCount;
  440.     this.treebox.rowCountChanged(this.rowCount - 1, 1);
  441.     this.treebox.ensureRowIsVisible(this.rowCount - 1);
  442.  
  443.     this.editType   = "create";
  444.     this.editParent = gRemotePath.value;
  445.     setTimeout("gRemoteTree.startEditing(remoteTree.rowCount - 1, gRemoteTree.columns['remotename'])", 0);
  446.   },
  447.  
  448.   remove : function() {
  449.     if (!gFtp.isConnected || !isReady() || this.selection.count == 0 || this.rowCount == 0) {
  450.       return;
  451.     }
  452.  
  453.     if (gRemoteTree.view.selection.count > 1) {                                                 // deleting multiple
  454.       if (!window.confirm(gStrbundle.getFormattedString("confirmDelete2", [gRemoteTree.view.selection.count]))) {
  455.         return;
  456.       }
  457.     } else if (this.data[gRemoteTree.view.selection.currentIndex].isDirectory()) {              // deleting a directory
  458.       if (!window.confirm(gStrbundle.getFormattedString("confirmDelete3", [this.data[gRemoteTree.view.selection.currentIndex].leafName]))) {
  459.         return;
  460.       }
  461.     } else {                                                                                    // deleting a file
  462.       if (!window.confirm(gStrbundle.getFormattedString("confirmDelete", [this.data[gRemoteTree.view.selection.currentIndex].leafName]))) {
  463.         return;
  464.       }
  465.     }
  466.  
  467.     var last  = true;
  468.  
  469.     gFtp.beginCmdBatch();
  470.  
  471.     for (var x = 0; x < this.rowCount; ++x) {
  472.       if (this.selection.isSelected(x)) {
  473.         if (last) {
  474.           gFtp.changeWorkingDirectory(gRemotePath.value);
  475.         }
  476.  
  477.         gFtp.remove(this.data[x].isDirectory(),
  478.                     this.data[x].path,
  479.                     (last && gRefreshMode) ? "remoteTree.refresh()" : "");
  480.         last = false;
  481.       }
  482.     }
  483.  
  484.     gFtp.endCmdBatch();
  485.   },
  486.  
  487.   rename : function() {
  488.     if (!gFtp.isConnected || !isReady() || this.selection.count == 0 || this.rowCount == 0) {
  489.       return;
  490.     }
  491.  
  492.     if (this.selection.currentIndex < 0 || this.selection.currentIndex >= this.rowCount) {
  493.       this.selection.currentIndex = this.rowCount - 1;
  494.     }
  495.  
  496.     this.displayData[this.selection.currentIndex].origLeafName = this.data[this.selection.currentIndex].leafName;
  497.     this.displayData[this.selection.currentIndex].origPath     = this.data[this.selection.currentIndex].path;
  498.  
  499.     if (this.searchMode == 2) {
  500.       this.displayData[this.selection.currentIndex].path = this.displayData[this.selection.currentIndex].leafName;
  501.       this.treebox.invalidateRow(this.selection.currentIndex);
  502.     }
  503.  
  504.     this.editType   = "rename";
  505.     this.editParent = gRemotePath.value;
  506.     gRemoteTree.startEditing(this.selection.currentIndex, gRemoteTree.columns["remotename"]);
  507.   },
  508.  
  509.   isEditable : function(row, col) {
  510.     var canEdit = row >= 0 && row < this.data.length && col.id == "remotename";
  511.     this.isEditing = canEdit;
  512.     return canEdit;
  513.   },
  514.  
  515.   setCellText : function(row, col, val) {
  516.     if (!this.isEditing || this.editParent != gRemotePath.value) {                              // for some reason, this is called twice - so we prevent this
  517.       return;
  518.     }
  519.  
  520.     this.isEditing = false;
  521.     if (this.editType == "rename") {
  522.       if (this.data[row].leafName == val) {
  523.         // do nothing
  524.       } else {
  525.         var path       = this.data[row].path;
  526.         var newName    = val;
  527.  
  528.         if (!newName) {
  529.           return;
  530.         }
  531.  
  532.         for (var x = 0; x < this.rowCount; ++x) {
  533.           if (this.data[x].leafName == newName) {
  534.             error(gStrbundle.getString("renameFail"));
  535.             this.displayData[row].leafName = val;
  536.             this.treebox.invalidateRow(row);
  537.             setTimeout("gRemoteTree.startEditing(" + row + ", gRemoteTree.columns['remotename'])", 0);
  538.             return;
  539.           }
  540.         }
  541.  
  542.         if (path.charAt(path.length - 1) == '/') {
  543.           path = path.substring(path.length - 1);
  544.         }
  545.  
  546.         newName = path.substring(0, path.lastIndexOf('/')) + '/' + newName;
  547.  
  548.         var rowDiff        = this.treebox.getLastVisibleRow() - row;
  549.         var self           = this;
  550.         var renameCallback = function() {
  551.           for (var x = 0; x < self.rowCount; ++x) {
  552.             if (self.data[x].leafName == val) {
  553.               self.selection.select(x);
  554.               self.treebox.ensureRowIsVisible(rowDiff + x - 1 < self.rowCount ? rowDiff + x - 1 : self.rowCount - 1);
  555.               break;
  556.             }
  557.           }
  558.         };
  559.         remoteTree.extraCallback = renameCallback;
  560.  
  561.         var errorCallback = function() {
  562.           setTimeout("gRemoteTree.startEditing(" + row + ", gRemoteTree.columns['remotename'])", 0);
  563.         };
  564.         remoteTree.errorCallback = errorCallback;
  565.  
  566.         this.displayData[row].leafName = val;
  567.         this.treebox.invalidateRow(row);
  568.         gFtp.rename(path, newName, gRefreshMode ? "remoteTree.refresh()" : "", this.data[row].isDirectory());
  569.       }
  570.     } else if (this.editType == "create") {
  571.       if (val) {
  572.         var self           = this;
  573.         var createCallback = function() {
  574.           for (var x = 0; x < self.rowCount; ++x) {
  575.             if (self.data[x].leafName == val) {
  576.               self.selection.select(x);
  577.               self.treebox.ensureRowIsVisible(x);
  578.               break;
  579.             }
  580.           }
  581.         };
  582.         remoteTree.extraCallback = createCallback;
  583.  
  584.         var errorCallback = function() {
  585.           self.data[row].leafName        = val;
  586.           self.displayData[row].leafName = val;
  587.           self.treebox.invalidateRow(row);
  588.           setTimeout("gRemoteTree.startEditing(remoteTree.rowCount - 1, gRemoteTree.columns['remotename'])", 0);
  589.         };
  590.         remoteTree.errorCallback = errorCallback;
  591.  
  592.         this.displayData[row].leafName = val;
  593.         this.treebox.invalidateRow(row);
  594.         if (this.data[row].isDir) {
  595.           gFtp.makeDirectory(gFtp.constructPath(gRemotePath.value, val), gRefreshMode ? "remoteTree.refresh()" : "");
  596.         } else {
  597.           gFtp.makeBlankFile(gFtp.constructPath(gRemotePath.value, val), gRefreshMode ? "remoteTree.refresh()" : "");
  598.         }
  599.       } else {
  600.         --this.rowCount;
  601.         this.data.splice(this.rowCount, 1);
  602.         this.displayData.splice(this.rowCount, 1);
  603.         this.treebox.rowCountChanged(this.rowCount, -1);
  604.       }
  605.     }
  606.   },
  607.  
  608.   showProperties : function(recursive) {
  609.     if (!gFtp.isConnected || !isReady() || this.selection.count == 0 || this.rowCount == 0) {
  610.       return;
  611.     }
  612.  
  613.     if (this.selection.currentIndex < 0 || this.selection.currentIndex >= this.rowCount) {
  614.       this.selection.currentIndex = this.rowCount - 1;
  615.     }
  616.  
  617.     this.recursiveFolderData = { type: "remote", nFolders: 0, nFiles: 0, nSize: 0, files: new Array() };
  618.  
  619.     if (this.selection.count > 1) {                                                             // multiple files
  620.       var last = true;
  621.  
  622.       for (var x = 0; x < this.rowCount; ++x) {
  623.         if (this.selection.isSelected(x)) {
  624.           if (this.data[x].isDirectory()) {
  625.             ++this.recursiveFolderData.nFolders;
  626.  
  627.             if (recursive) {
  628.               var remotePath = this.data[x].path;
  629.  
  630.               if (last) {
  631.                 gFtp.beginCmdBatch();
  632.               }
  633.  
  634.               gFtp.list(remotePath, "remoteTree.getRecursiveFolderData('" + remotePath.replace(/'/g, "\\'") + "'," + last + ")", true, true);
  635.               last = false;
  636.             }
  637.  
  638.           } else {
  639.             ++this.recursiveFolderData.nFiles;
  640.           }
  641.  
  642.           this.recursiveFolderData.files.push(this.data[x]);
  643.           this.recursiveFolderData.nSize += this.data[x].fileSize;
  644.         }
  645.       }
  646.  
  647.       if (last) {
  648.         this.showMultipleProperties();
  649.       } else {
  650.         gFtp.endCmdBatch();
  651.       }
  652.  
  653.       return;
  654.     }
  655.  
  656.     var index = this.selection.currentIndex;
  657.     var path  = this.data[index].path;
  658.  
  659.     if (this.data[index].isDirectory() && recursive) {                                          // directory
  660.       gFtp.list(path, "remoteTree.getRecursiveFolderData('" + path.replace(/'/g, "\\'") + "', true, true)", true);
  661.       this.recursiveFolderData.nSize += this.data[index].fileSize;
  662.       return;
  663.     }
  664.  
  665.     var params = { path            : path,
  666.                    leafName        : this.data[index].leafName,
  667.                    fileSize        : this.data[index].fileSize,
  668.                    date            : this.data[index].date,
  669.                    origPermissions : this.data[index].permissions,
  670.                    writable        : 'disabled',
  671.                    hidden          : 'disabled',
  672.                    isDirectory     : this.data[index].isDirectory(),
  673.                    user            : this.data[index].user,
  674.                    group           : this.data[index].group,
  675.                    permissions     : "",
  676.                    webHost         : gWebHost,
  677.                    prefix          : gPrefix,
  678.                    isSymlink       : this.data[index].isSymlink(),
  679.                    symlink         : this.data[index].symlink,
  680.                    featXMD5        : gFtp.featXMD5,
  681.                    featXSHA1       : gFtp.featXSHA1,
  682.                    gFtp            : gFtp };
  683.  
  684.     window.openDialog("chrome://fireftp/content/properties.xul", "properties", "chrome,modal,dialog,resizable,centerscreen", params);
  685.  
  686.     if (params.permissions && gFtp.isConnected && isReady()) {                                  // permissions were changed; CHMOD!
  687.       var rowDiff        = this.treebox.getLastVisibleRow() - index;
  688.       var name           = this.data[index].leafName;
  689.       var self           = this;
  690.       var propsCallback  = function() {
  691.         for (var x = 0; x < self.rowCount; ++x) {
  692.           if (self.data[x].leafName == name) {
  693.             self.selection.select(x);
  694.             self.treebox.ensureRowIsVisible(rowDiff + x - 1 < self.rowCount ? rowDiff + x - 1 : self.rowCount - 1);
  695.             break;
  696.           }
  697.         }
  698.       };
  699.       remoteTree.extraCallback = propsCallback;
  700.  
  701.       gFtp.changePermissions(params.permissions, path, gRefreshMode ? "remoteTree.refresh()" : "");
  702.     }
  703.   },
  704.  
  705.   recursiveFolderData    : new Object(),
  706.   getRecursiveFolderData : function(parent, last, showDir) {
  707.     var files = gFtp.listData;
  708.  
  709.     for (var x = 0; x < files.length; ++x) {
  710.       var remotePath = gFtp.constructPath(parent, files[x].leafName);
  711.  
  712.       if (files[x].isDirectory()) {
  713.         ++this.recursiveFolderData.nFolders;
  714.         gFtp.list(remotePath, "remoteTree.getRecursiveFolderData('" + remotePath.replace(/'/g, "\\'") + "'," + last + "," + showDir + ")", true, true);
  715.         last = false;
  716.       } else {
  717.         ++this.recursiveFolderData.nFiles;
  718.       }
  719.  
  720.       this.recursiveFolderData.files.push(files[x]);
  721.       this.recursiveFolderData.nSize += files[x].fileSize;
  722.     }
  723.  
  724.     if (last) {
  725.       this.showMultipleProperties(showDir);
  726.     }
  727.   },
  728.  
  729.   showMultipleProperties : function(dir) {
  730.     var params;
  731.     var path;
  732.  
  733.     if (dir) {
  734.       var index = this.selection.currentIndex;
  735.       path      = this.data[index].path;
  736.  
  737.       params = { path                : path,
  738.                  leafName            : this.data[index].leafName,
  739.                  fileSize            : 0,
  740.                  date                : this.data[index].date,
  741.                  origPermissions     : this.data[index].permissions,
  742.                  writable            : 'disabled',
  743.                  hidden              : 'disabled',
  744.                  isDirectory         : this.data[index].isDirectory(),
  745.                  user                : this.data[index].user,
  746.                  group               : this.data[index].group,
  747.                  permissions         : "",
  748.                  isSymlink           : this.data[index].isSymlink(),
  749.                  symlink             : this.data[index].symlink,
  750.                  multipleFiles       : false,
  751.                  recursiveFolderData : this.recursiveFolderData,
  752.                  applyTo             : { type: "remote", thisFile: true, folders: false, files: false } };
  753.     } else {
  754.       params = { multipleFiles       : true,
  755.                  recursiveFolderData : this.recursiveFolderData,
  756.                  permissions         : "",
  757.                  applyTo             : { folders: false, files: false } };
  758.     }
  759.  
  760.     window.openDialog("chrome://fireftp/content/properties.xul", "properties", "chrome,modal,dialog,resizable,centerscreen", params);
  761.  
  762.     if (params.permissions && gFtp.isConnected) {                                               // permissions were changed; CHMOD!
  763.       var last = true;
  764.  
  765.       gFtp.beginCmdBatch();
  766.  
  767.       if (dir && params.applyTo.thisFile) {
  768.         gFtp.changePermissions(params.permissions, path, gRefreshMode ? "remoteTree.refresh()" : "");
  769.         last = false;
  770.       }
  771.  
  772.       for (var x = 0; x < this.recursiveFolderData.files.length; ++x) {
  773.         if ((this.recursiveFolderData.files[x].isDirectory() && params.applyTo.folders)
  774.         || (!this.recursiveFolderData.files[x].isDirectory() && params.applyTo.files)) {
  775.           gFtp.changePermissions(params.permissions, this.recursiveFolderData.files[x].path, (last && gRefreshMode) ? "remoteTree.refresh()" : "");
  776.           last = false;
  777.         }
  778.       }
  779.  
  780.       gFtp.endCmdBatch();
  781.     }
  782.   },
  783.  
  784.   // ************************************************* mouseEvent *****************************************************
  785.  
  786.   dblClick : function(event) {
  787.     if (!gFtp.isConnected || event.button != 0 || event.originalTarget.localName != "treechildren" || this.selection.count == 0) {
  788.       return;
  789.     }
  790.  
  791.     if (this.selection.currentIndex < 0 || this.selection.currentIndex >= this.rowCount) {
  792.       this.selection.currentIndex = this.rowCount - 1;
  793.     }
  794.  
  795.     if (this.data[this.selection.currentIndex].isDirectory()) {                                 // if it's a directory
  796.       if (!isReady()) {
  797.         return;
  798.       }
  799.                                                                                                 // navigate to it
  800.       remoteDirTree.changeDir(this.data[this.selection.currentIndex].path);
  801.     } else if (this.data[this.selection.currentIndex].isSymlink()) {                            // if it's a symbolic link
  802.       if (gFtp.isListing()) {
  803.         return;
  804.       }
  805.  
  806.       var linkedPath = this.data[this.selection.currentIndex].path;
  807.       var linkedFile = this.data[this.selection.currentIndex].symlink;
  808.       var parentPath = gRemotePath.value;
  809.  
  810.       while (linkedFile.indexOf("./") == 0 || linkedFile.indexOf("../") == 0) {
  811.         if (linkedFile.indexOf("./") == 0) {
  812.           linkedFile = linkedFile.substring(2);
  813.         } else {
  814.           linkedFile = linkedFile.substring(3);
  815.           parentPath = parentPath.substring(0, parentPath.lastIndexOf('/') ? parentPath.lastIndexOf('/') : 1);
  816.         }
  817.       }
  818.  
  819.       if (linkedFile.indexOf("/") != 0) {
  820.         linkedFile = gFtp.constructPath(parentPath, linkedFile);
  821.       }
  822.  
  823.       parentPath = linkedFile.substring(0, linkedFile.lastIndexOf('/') ? linkedFile.lastIndexOf('/') : 1);
  824.  
  825.       var self    = this;
  826.       var cwdFunc = function(success) {
  827.         if (success) {
  828.           return;
  829.         }
  830.  
  831.         var cwd2Func = function(success2) {
  832.           var listFunc = function() {
  833.             for (var x = 0; x < gFtp.listData.length; ++x) {
  834.               if (gFtp.listData[x].path == linkedFile) {
  835.                 new transfer().start(true, gFtp.listData[x], '', parentPath);
  836.                 return;
  837.               }
  838.             }
  839.  
  840.             gFtp.changeWorkingDirectory(linkedPath);
  841.           };
  842.  
  843.           if (success2) {
  844.             gFtp.list(parentPath, listFunc);
  845.           } else {
  846.             gFtp.changeWorkingDirectory(linkedPath);
  847.           }
  848.         };
  849.  
  850.         if (gFtp.currentWorkingDir != parentPath) {
  851.           gFtp.changeWorkingDirectory(parentPath, cwd2Func);
  852.         } else {
  853.           cwd2Func(true);
  854.         }
  855.       };
  856.  
  857.       if (gFtp.currentWorkingDir != linkedFile) {
  858.         gFtp.changeWorkingDirectory(linkedFile, cwdFunc);
  859.       } else {
  860.         remoteDirTree.changeDir(gFtp.currentWorkingDir);
  861.       }
  862.     } else {
  863.       if (gOpenMode) {
  864.         this.launch();
  865.       } else {
  866.         new transfer().start(true);                                                             // else download the file
  867.       }
  868.     }
  869.   },
  870.  
  871.   click : function(event) {
  872.     if (gFtp.isConnected && event.button == 1 && !$('remotePasteContext').disabled) {           // middle-click paste
  873.       this.paste();
  874.     }
  875.   },
  876.  
  877.   createContextMenu : function() {
  878.     if (this.selection.currentIndex < 0 || this.selection.currentIndex >= this.rowCount) {
  879.       this.selection.currentIndex = this.rowCount - 1;
  880.     }
  881.  
  882.     for (var x = $('remoteOpenWithMenu').childNodes.length - 1; x >= 0; --x) {                  // clear out the menus
  883.       $('remoteOpenWithMenu').removeChild($('remoteOpenWithMenu').childNodes.item(x));
  884.     }
  885.  
  886.     for (var x = $('remoteFXPMenu').childNodes.length - 1; x >= 0; --x) {
  887.       $('remoteFXPMenu').removeChild($('remoteFXPMenu').childNodes.item(x));
  888.     }
  889.  
  890.     $('remoteOpenCont').collapsed    =               this.searchMode != 2;
  891.     $('remoteOpenContSep').collapsed =               this.searchMode != 2;
  892.     $('remoteCutContext').setAttribute("disabled",   this.searchMode == 2 || !gFtp.isConnected);
  893.     $('remotePasteContext').setAttribute("disabled", this.searchMode == 2 || !gFtp.isConnected || !this.pasteFiles.length);
  894.     $('remoteCreateDir').setAttribute("disabled",    this.searchMode == 2 || !gFtp.isConnected);
  895.     $('remoteCreateFile').setAttribute("disabled",   this.searchMode == 2 || !gFtp.isConnected);
  896.  
  897.     if (this.selection.currentIndex == -1) {
  898.       return;
  899.     }
  900.  
  901.     var hasDir = false;
  902.     for (var x = 0; x < this.rowCount; ++x) {
  903.       if (this.selection.isSelected(x)) {
  904.         if (this.data[x].isDirectory()) {
  905.           hasDir = true;
  906.           break;
  907.         }
  908.       }
  909.     }
  910.  
  911.     $('remoteRecursiveProperties').setAttribute("disabled", !hasDir || !gFtp.isConnected);
  912.  
  913.     var extension = this.getExtension(this.data[this.selection.currentIndex].leafName);
  914.     var item;
  915.     var found     = false;
  916.  
  917.     var self = this;
  918.     var contextMenuHelper = function(x, y) {
  919.       found = true;
  920.       var program = localFile.init(gPrograms[x].programs[y].executable);
  921.  
  922.       if (!program) {
  923.         return;
  924.       }
  925.  
  926.       var fileURI = gIos.newFileURI(program);
  927.       item        = document.createElement("menuitem");
  928.       item.setAttribute("class",     "menuitem-iconic");
  929.       item.setAttribute("image",     "moz-icon://" + fileURI.spec + "?size=16");
  930.       item.setAttribute("label",     gPrograms[x].programs[y].name);
  931.       item.setAttribute("oncommand", "remoteLaunchProgram(" + x + ", " + y + ", " + self.selection.currentIndex + ")");
  932.       $('remoteOpenWithMenu').appendChild(item);
  933.     };
  934.  
  935.     for (var x = 0; x < gPrograms.length; ++x) {
  936.       if (gPrograms[x].extension.toLowerCase() == extension.toLowerCase()) {
  937.         for (var y = 0; y < gPrograms[x].programs.length; ++y) {
  938.           contextMenuHelper(x, y);
  939.         }
  940.  
  941.         break;
  942.       }
  943.     }
  944.  
  945.     for (var x = 0; x < gPrograms.length; ++x) {
  946.       if (gPrograms[x].extension == "*.*") {
  947.         for (var y = 0; y < gPrograms[x].programs.length; ++y) {
  948.           contextMenuHelper(x, y);
  949.         }
  950.  
  951.         break;
  952.       }
  953.     }
  954.  
  955.     if (found) {
  956.       item = document.createElement("menuseparator");
  957.       $('remoteOpenWithMenu').appendChild(item);
  958.     }
  959.  
  960.     item = document.createElement("menuitem");
  961.     item.setAttribute("label", gStrbundle.getString("chooseProgram"));
  962.     item.setAttribute("oncommand", "chooseProgram(true)");
  963.     $('remoteOpenWithMenu').appendChild(item);
  964.  
  965.     for (var x = 0; x < gSiteManager.length; ++x) {
  966.       if (gSiteManager[x].account != gAccount) {
  967.         item = document.createElement("menuitem");
  968.         item.setAttribute("class",     "menuitem-iconic");
  969.         item.setAttribute("image",     "chrome://fireftp/skin/icons/logo.png");
  970.         item.setAttribute("label",     gSiteManager[x].account);
  971.         item.setAttribute("oncommand", "fxp('" + gSiteManager[x].account + "')");
  972.         $('remoteFXPMenu').appendChild(item);
  973.       }
  974.     }
  975.   },
  976.  
  977.   mouseOver : function(event) {                                                                 // display remote folder info
  978.     if (this.rowCount) {
  979.       $('statustxt').label = gStrbundle.getString("remoteListing") + " " + gStrbundle.getFormattedString("objects", [this.rowCount])
  980.                            + (this.remoteSize < 0 ? "" : ", " + commas(this.remoteSize));
  981.     } else {
  982.       $('statustxt').label = gStrbundle.getString("remoteListingNoObjects");
  983.     }
  984.   },
  985.  
  986.   // ************************************************* keyEvent *****************************************************
  987.  
  988.   keyPress : function(event) {
  989.     if (!gFtp.isConnected) {
  990.       return;
  991.     }
  992.  
  993.     if (gRemoteTree.editingRow != -1) {
  994.       if (event.keyCode == 27) {
  995.         if (this.editType == "create") {
  996.           this.setCellText(-1, "", "");
  997.         } else {
  998.           this.displayData[gRemoteTree.editingRow].leafName = this.displayData[gRemoteTree.editingRow].origLeafName;
  999.           this.displayData[gRemoteTree.editingRow].path     = this.displayData[gRemoteTree.editingRow].origPath;
  1000.           this.treebox.invalidateRow(gRemoteTree.editingRow);
  1001.         }
  1002.       }
  1003.  
  1004.       return;
  1005.     }
  1006.  
  1007.     if (this.selection.currentIndex < 0 || this.selection.currentIndex >= this.rowCount) {
  1008.       this.selection.currentIndex = this.rowCount - 1;
  1009.     }
  1010.  
  1011.     var accelKey = testAccelKey(event);
  1012.  
  1013.     if (event.keyCode == 13 && this.selection.count != 0) {                                     // enter
  1014.       if (this.selection.count == 1 && this.data[this.selection.currentIndex].isDirectory()) {  // if it's a directory
  1015.         if (!isReady()) {
  1016.           return;
  1017.         }
  1018.                                                                                                 // navigate to it
  1019.         remoteDirTree.changeDir(this.data[this.selection.currentIndex].path);
  1020.       } else {
  1021.         if (gOpenMode) {
  1022.           this.launch();
  1023.         } else {
  1024.           new transfer().start(true);                                                           // else retrieve a file
  1025.         }
  1026.       }
  1027.     } else if (accelKey && (event.which == 65 || event.which == 97)) {
  1028.       event.preventDefault();                                                                   // accel-a: select all
  1029.       this.selection.selectAll();
  1030.     } else if (event.ctrlKey && event.keyCode == 32 && this.selection.count != 0) {             // ctrl-space, select or deselect
  1031.       this.selection.toggleSelect(this.selection.currentIndex);
  1032.     } else if (event.keyCode  == 8) {                                                           // backspace
  1033.       event.preventDefault();
  1034.       remoteDirTree.cdup();
  1035.     } else if (event.keyCode  == 116) {                                                         // F5
  1036.       event.preventDefault();
  1037.       this.refresh();
  1038.     } else if (event.keyCode  == 113 && this.selection.count != 0) {                            // F2
  1039.       this.rename();
  1040.     } else if (event.charCode == 100 && accelKey) {                                             // accel-d
  1041.       event.preventDefault();
  1042.       this.create(true);
  1043.     } else if (event.charCode == 110 && accelKey) {                                             // accel-n
  1044.       event.preventDefault();
  1045.       this.create(false);
  1046.     } else if (event.keyCode  == 46 && this.selection.count != 0) {                             // del
  1047.       this.remove();
  1048.     } else if (event.keyCode  == 93) {                                                          // display context menu
  1049.       var x = {};    var y = {};    var width = {};    var height = {};
  1050.       this.treebox.getCoordsForCellItem(this.selection.currentIndex, this.treebox.columns["remotename"], "text", x, y, width, height);
  1051.       $('remotemenu').showPopup(gRemoteTreeChildren, gRemoteTreeChildren.boxObject.x + 75, gRemoteTreeChildren.boxObject.y + y.value + 5, "context");
  1052.     } else if (event.charCode == 112 && accelKey && this.selection.count != 0) {                // accel-p
  1053.       event.preventDefault();
  1054.       this.showProperties(false);
  1055.     } else if (event.charCode == 120 && accelKey && this.selection.count != 0) {                // accel-x
  1056.       event.preventDefault();
  1057.       this.cut();
  1058.     } else if (event.charCode == 118 && accelKey) {                                             // accel-v
  1059.       event.preventDefault();
  1060.       this.paste();
  1061.     } else if (event.charCode == 111 && accelKey) {                                             // accel-o
  1062.       event.preventDefault();
  1063.       this.launch();
  1064.     } else if (event.charCode == 117 && accelKey) {                                             // accel-u
  1065.       event.preventDefault();
  1066.       this.copyUrl(true);
  1067.     }
  1068.   },
  1069.  
  1070.   // ************************************************* cut, copy, paste *****************************************************
  1071.  
  1072.   pasteFiles : new Array(),
  1073.   oldParent  : "",
  1074.  
  1075.   cut : function() {
  1076.     if (!gFtp.isConnected || this.selection.count == 0 || this.searchMode == 2) {
  1077.       return;
  1078.     }
  1079.  
  1080.     this.pasteFiles = new Array();
  1081.     this.oldParent  = gRemotePath.value;
  1082.  
  1083.     for (var x = 0; x < this.rowCount; ++x) {                                                   // put files to be cut/copied in an array to be pasted
  1084.       if (this.selection.isSelected(x)) {
  1085.         this.pasteFiles.push(this.data[x]);
  1086.         this.data[x].isCut = true;
  1087.         this.treebox.invalidateRow(x);
  1088.       }
  1089.     }
  1090.  
  1091.     $('remotePasteContext').setAttribute("disabled", false);                                    // enable pasting
  1092.   },
  1093.  
  1094.   paste : function(dest) {
  1095.     if (!gFtp.isConnected || this.pasteFiles.length == 0 || this.searchMode == 2) {
  1096.       return;
  1097.     }
  1098.  
  1099.     var newParent = dest ? dest          : gRemotePath.value;
  1100.     var files     = dest ? gFtp.listData : this.data;
  1101.  
  1102.     for (var x = 0; x < this.pasteFiles.length; ++x) {
  1103.       var newParentSlash = newParent               + (newParent.charAt(newParent.length - 1)                             != "/" ? "/" : '');
  1104.       var pasteFileSlash = this.pasteFiles[x].path + (this.pasteFiles[x].path.charAt(this.pasteFiles[x].path.length - 1) != "/" ? "/" : '');
  1105.  
  1106.       if (this.pasteFiles[x].isDirectory() && newParentSlash.indexOf(pasteFileSlash) == 0) {    // can't copy into a subdirectory of itself
  1107.         doAlert(gStrbundle.getString("copySubdirectory"));
  1108.         return;
  1109.       }
  1110.     }
  1111.  
  1112.     var prompt     = true;
  1113.     var skipAll    = false;
  1114.     var anyFolders = false;
  1115.  
  1116.     gFtp.beginCmdBatch();
  1117.  
  1118.     if (!dest) {
  1119.       gFtp.changeWorkingDirectory(newParent);
  1120.     }
  1121.  
  1122.     for (var x = 0; x < this.pasteFiles.length; ++x) {
  1123.       var newPath     = gFtp.constructPath(newParent, this.pasteFiles[x].leafName);
  1124.       var exists      = false;
  1125.       var isDirectory = false;
  1126.       var newFile;
  1127.  
  1128.       if (this.pasteFiles[x].isDirectory()) {
  1129.         anyFolders = true;
  1130.       }
  1131.  
  1132.       for (var y = 0; y < files.length; ++y) {
  1133.         if (files[y].leafName == this.pasteFiles[x].leafName) {
  1134.           exists      = true;
  1135.           newFile     = files[y];
  1136.           isDirectory = files[y].isDirectory();
  1137.           break;
  1138.         }
  1139.       }
  1140.  
  1141.       if (exists && skipAll) {
  1142.         continue;
  1143.       }
  1144.  
  1145.       if (exists && (isDirectory || this.pasteFiles[x].isDirectory())) {
  1146.         error(gStrbundle.getFormattedString("pasteErrorFile", [this.pasteFiles[x].path]));
  1147.         continue;
  1148.       }
  1149.  
  1150.       if (exists && prompt) {
  1151.         var params = { response         : 0,
  1152.                        fileName         : newPath,
  1153.                        resume           : true,
  1154.                        replaceResume    : true,
  1155.                        existingSize     : newFile.fileSize,
  1156.                        existingDate     : newFile.lastModifiedTime,
  1157.                        newSize          : this.pasteFiles[x].fileSize,
  1158.                        newDate          : this.pasteFiles[x].lastModifiedTime,
  1159.                        timerEnable      : false };
  1160.  
  1161.         window.openDialog("chrome://fireftp/content/confirmFile.xul", "confirmFile", "chrome,modal,dialog,resizable,centerscreen", params);
  1162.  
  1163.         if (params.response == 2) {
  1164.           prompt = false;
  1165.         } else if (params.response == 3) {
  1166.           continue;
  1167.         } else if (params.response == 4 || params.response == 0) {
  1168.           return;
  1169.         } else if (params.response == 5) {
  1170.           skipAll = true;
  1171.           continue;
  1172.         }
  1173.       }
  1174.  
  1175.       if (exists) {
  1176.         gFtp.remove(false, newPath);
  1177.       }
  1178.  
  1179.       var self          = this;
  1180.       var oldParent     = this.oldParent;
  1181.       var pasteCallback = function() { self.pasteCallback(oldParent, newParent, anyFolders, dest); };
  1182.       gFtp.rename(this.pasteFiles[x].path, newPath, x == this.pasteFiles.length - 1 && gRefreshMode ? pasteCallback : "", this.pasteFiles[x].isDirectory());
  1183.     }
  1184.  
  1185.     this.pasteFiles = new Array();
  1186.     $('remotePasteContext').setAttribute("disabled", true);
  1187.  
  1188.     gFtp.endCmdBatch();
  1189.   },
  1190.  
  1191.   pasteCallback : function(oldParent, newParent, anyFolders, dest) {
  1192.     remoteDirTree.addDirtyList(oldParent);
  1193.     remoteDirTree.addDirtyList(newParent);
  1194.  
  1195.     if (anyFolders) {
  1196.       var refreshIndex = dest ? remoteDirTree.indexOfPath(newParent) : remoteDirTree.indexOfPath(oldParent);
  1197.  
  1198.       if (refreshIndex != -1) {
  1199.         if (remoteDirTree.data[refreshIndex].open) {
  1200.           var self           = this;
  1201.           var pasteCallback2 = function() { self.pasteCallback2(oldParent, newParent, dest); };
  1202.           remoteDirTree.extraCallback = pasteCallback2;
  1203.  
  1204.           remoteDirTree.toggleOpenState(refreshIndex, true);                                       // close it up
  1205.           remoteDirTree.data[refreshIndex].children = null;                                        // reset its children
  1206.           remoteDirTree.toggleOpenState(refreshIndex);                                             // and open it up again
  1207.           return;
  1208.         } else {
  1209.           remoteDirTree.data[refreshIndex].children = null;                                        // reset its children
  1210.           remoteDirTree.data[refreshIndex].empty    = false;
  1211.           remoteDirTree.treebox.invalidateRow(refreshIndex);
  1212.         }
  1213.  
  1214.         var refreshIndex2 = dest ? remoteDirTree.indexOfPath(oldParent) : remoteDirTree.indexOfPath(newParent);
  1215.  
  1216.         if (refreshIndex2 == -1) {
  1217.           remoteDirTree.changeDir(dest ? oldParent : newParent);
  1218.           return;
  1219.         } else {
  1220.           remoteDirTree.selection.select(refreshIndex2);
  1221.         }
  1222.       }
  1223.     }
  1224.  
  1225.     this.refresh();
  1226.   },
  1227.  
  1228.   pasteCallback2 : function(oldParent, newParent, dest) {
  1229.     var refreshIndex2 = dest ? remoteDirTree.indexOfPath(oldParent) : remoteDirTree.indexOfPath(newParent);
  1230.  
  1231.     if (refreshIndex2 == -1) {
  1232.       remoteDirTree.changeDir(dest ? oldParent : newParent);
  1233.       return;
  1234.     } else {
  1235.       remoteDirTree.selection.select(refreshIndex2);
  1236.     }
  1237.  
  1238.     this.refresh();
  1239.   },
  1240.  
  1241.   canDrop : function(index, orient) {
  1242.     if (!gFtp.isConnected || !dragObserver.origin
  1243.       || (dragObserver.origin.indexOf('remote') != -1 && index == -1)
  1244.       || (dragObserver.origin.indexOf('remote') != -1 && !this.data[index].isDirectory())) {
  1245.       return false;
  1246.     }
  1247.  
  1248.     if (dragObserver.origin == 'remotetreechildren') {                                          // don't drag onto itself
  1249.       for (var x = 0; x < this.rowCount; ++x) {
  1250.         if (this.selection.isSelected(x) && index == x) {
  1251.           return false;
  1252.         }
  1253.       }
  1254.     }
  1255.  
  1256.     return true;
  1257.   },
  1258.  
  1259.   drop : function(index, orient) {
  1260.     if (dragObserver.origin == 'remotetreechildren') {
  1261.       this.cut();
  1262.  
  1263.       var self = this;
  1264.       var path = this.data[index].path;
  1265.       var func = function() { self.paste(path); };
  1266.       gFtp.list(this.data[index].path, func, true);
  1267.     } else if (dragObserver.origin == 'localtreechildren') {
  1268.       if (!dragObserver.overName || index == -1 || !this.data[index].isDirectory()) {
  1269.         new transfer().start(false);
  1270.       } else {
  1271.         var self                  = this;
  1272.         var path                  = this.data[index].path;
  1273.         var transferObj           = new transfer();
  1274.         transferObj.remoteRefresh = gRemotePath.value;
  1275.         var func                  = function() { transferObj.start(false, '', '', path); };
  1276.         gFtp.list(this.data[index].path, func, true);
  1277.       }
  1278.     } else if (dragObserver.origin == 'external') {
  1279.       var regular               = !dragObserver.overName || index == -1 || !this.data[index].isDirectory();
  1280.       var transferObj           = new transfer();
  1281.       transferObj.remoteRefresh = gRemotePath.value;
  1282.  
  1283.       for (var x = 0; x < dragObserver.externalFiles.length; ++x) {
  1284.         var droppedFile    = dragObserver.externalFiles[x];
  1285.         var fileParent     = droppedFile.parent ? droppedFile.parent.path : "";
  1286.  
  1287.         if (regular) {
  1288.           transferObj.start(false, droppedFile, fileParent, gRemotePath.value);
  1289.         } else {
  1290.           this.dropHelper(transferObj, droppedFile, fileParent, index);
  1291.         }
  1292.  
  1293.         if (transferObj.cancel) {
  1294.           break;
  1295.         }
  1296.       }
  1297.     }
  1298.   },
  1299.  
  1300.   dropHelper : function(transferObj, droppedFile, fileParent, index) {
  1301.     var self       = this;
  1302.     var remotePath = this.data[index].path;
  1303.     var func       = function() { transferObj.start(false, droppedFile, fileParent, remotePath); };
  1304.     gFtp.list(this.data[index].path, func, true);
  1305.   }
  1306. };
  1307.